package com.bz.websocket;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Objects;

import reactor.core.publisher.Mono;

/**
 * @author zhongyongbin
 * @since 2021/5/10
 */
@Component
public final class UserSessionManager {

    private static final int SESSION_EXPIRE_TIME_MINUTES = 15;

    private static final int SESSION_EXPIRE_TIME_REFRESH_DIFF_MINUTES = 3;

    private final Duration sessionExpireTime = Duration.ofMinutes(SESSION_EXPIRE_TIME_MINUTES);

    private final Duration sessionExpireTimeRefreshTime = Duration.ofMinutes(SESSION_EXPIRE_TIME_REFRESH_DIFF_MINUTES);

    private static final String SESSION_REDIS_STORAGE_KEY = "usession:%s";

    private final Cache<String, UserSession> sessionStorage = CacheBuilder
            .newBuilder()
            .expireAfterAccess(sessionExpireTime)
            .build();

    @Autowired
    private ReactiveStringRedisTemplate stringRedisTemplate;

    /**
     * 添加会话
     *
     * @param sessionId   会话 ID
     * @param userSession 用户会话
     * @return 会话添加结果
     */
    public Mono<Boolean> addSession(String sessionId, UserSession userSession) {
        sessionStorage.put(sessionId, userSession);

        Mono<Boolean> mono = stringRedisTemplate.opsForValue().set(getSessionRedisStorageKey(userSession.getUserId()), Self.getName(), sessionExpireTime);
        userSession.setExpireAt(System.currentTimeMillis() + sessionExpireTime.toMillis());

        return mono;
    }

    /**
     * 刷新过期时间
     *
     * @param sessionId 会话 ID
     * @return true 表示会话存在，false 标识会话不存在
     */
    public boolean refreshExpireTime(String sessionId) {
        UserSession userSession = sessionStorage.getIfPresent(sessionId);
        if (Objects.isNull(userSession)) {
            return false;
        }
        if (userSession.getExpireAt() - System.currentTimeMillis() < sessionExpireTimeRefreshTime.toMillis()) {
            stringRedisTemplate.expire(getSessionRedisStorageKey(userSession.getUserId()), sessionExpireTime);
        }

        return true;
    }

    /**
     * 会话是否存活
     *
     * @param sessionId 会话 ID
     * @return True 为会话存活，否则不存活
     */
    public boolean isSessionAlive(String sessionId) {
        UserSession userSession = sessionStorage.getIfPresent(sessionId);

        return Objects.nonNull(userSession);
    }

    private String getSessionRedisStorageKey(String userId) {
        return String.format(SESSION_REDIS_STORAGE_KEY, userId);
    }
}
